﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Util;
using Word = Microsoft.Office.Interop.Word;
using System.Text.RegularExpressions;
using Microsoft.Office.Interop.Word;

namespace WORD_LIB
{
    public class WordSession
    {
        private static object m = Type.Missing;
        private static object otrue = true;
        private static object ofalse = false;
        private Word.Application word;
        private Word.Document doc;
        public WordSession()
        {
            word = new Word.Application();
        }

        public void Open(string path)
        {
            object opath = (object)path;
            doc = word.Documents.Open(opath, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m);
        }

        public void Save(string path)
        {
            object opath = (object)path;
            doc.SaveAs2(opath, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m, m);
        }

        public void Close()
        {
            ((_Document)doc).Close(m, m, m);
        }

        public void Quit()
        {
            ((_Application)word).Quit(ofalse, m, m);
        }

        public void Show()
        {
            word.Visible = true;
        }

        public void Hide()
        {
            word.Visible = false;
        }

        //int ParagraphIx = int.MinValue;

        private void ValidateParagraphIx(ref int paragraphIx, int max)
        {
            if (paragraphIx < 1) paragraphIx = 1;
            if (paragraphIx > max) paragraphIx = max;
        }

        public List<Tuple<int, int>> sectionsParagraphs = new List<Tuple<int, int>>();
        public List<Tuple<int, int>> sectionsWords = new List<Tuple<int, int>>();

        /// <summary>
        /// en este metodo voy a splitear el word entero en parrafos
        /// </summary>
        public void Split(Func<string, bool> test)
        {
            List<int> sectionBeginParagraphs = new List<int>();
            List<int> sectionBeginWords = new List<int>();
            int wordIx = 1;
            int totalParagraphs = doc.Paragraphs.Count;
            for (int paragraphIx = 1; paragraphIx <= totalParagraphs; paragraphIx++)
            {
                Logger.WriteLine("{0}/{1}", paragraphIx, totalParagraphs);
                var paragraph = doc.Paragraphs[paragraphIx];
                if (test(paragraph.Range.Text))
                {
                    paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdPink;
                    Logger.WriteLine("Verso encontrado: {0}", paragraph.Range.Text);
                    sectionBeginParagraphs.Add(paragraphIx);
                    sectionBeginWords.Add(wordIx);
                }
                wordIx += paragraph.Range.Words.Count;
            }
            int prev = sectionBeginParagraphs[0];
            sectionBeginParagraphs.RemoveAt(0);
            sectionsParagraphs = sectionBeginParagraphs.Select(
                x =>
                {
                    var tuple = new Tuple<int, int>(prev, x);
                    prev = x;
                    return tuple;
                }).ToList();
        }

        private void ExtractParsing(
            string text,
            Func<Key, int> getVerseCharsCount,
            Func<string, Key> getShortKey,
            Func<Key, Key> convertToFullKey,
            ref int charIx,
            List<Fragment> wordFragments,
            ref int searchFails,
            ref int unmanagedExceptions,
            Func<Key, string> getNVI,
            Func<Key, string> getRV)
        {
#if DEBUG_NUMBERED_BOOKS
			int num;
			if(int.TryParse(text[0].ToString(), out num))
				Debugger.Break();
#endif
            try
            {
                Key key = null;

                key = getShortKey(text);
                int keyLength = key.Length;
                key = convertToFullKey(key);
                try
                {
                    int charsCount = getVerseCharsCount(key);

                    //int totalLength = keyLength + 1 + charsCount;
                    //int begin = charIx;
                    //int end = charIx + totalLength;

                    int totalLength = keyLength + 1 + charsCount;
                    int begin = charIx + keyLength + 1;
                    int end = charIx + totalLength;

                    var range = doc.Range(begin, end);
                    string rangeText = range.Text;
                    wordFragments.Add(new Fragment(begin, end, key, range.Text));

                    string nviText = getNVI(key);
                    string rvText = getRV(key);

                    Logger.WriteLine("-----------------------------------------------------------------");
                    Logger.WriteLine("DOC: [{0}]", rangeText);
                    Logger.WriteLine("NVI: [{0}]", nviText);
                    Logger.WriteLine(" RV: [{0}]", rvText);

                    if (rangeText == nviText)
                    {
                        range.HighlightColorIndex = Word.WdColorIndex.wdBrightGreen;
                    }
                    else if (rangeText == rvText)
                    {
                        range.HighlightColorIndex = Word.WdColorIndex.wdDarkYellow;
                    }
                    else
                    {
                        range.HighlightColorIndex = Word.WdColorIndex.wdRed;
                    }

                    Logger.WriteLine("Verso encontrado: {0}", rangeText);
                }
                catch
                {
                    Logger.WriteLine("Falla al buscar el texto NVI: {0}", key);
                    //ExtractParsing(text, getVerseCharsCount, getShortKey, convertToFullKey, getKeyLength, ref charIx, wordFragments, ref searchFails, ref unmanagedExceptions);
                    searchFails++;
                }
            }
            catch
            {
                Logger.WriteLine("Falla al tratar de efectuar reemplazo a: {0}", text);
                unmanagedExceptions++;
            }
        }

        public List<Fragment> Extract(Func<string, bool> test,
                                        Func<Key, int> getVerseCharsCount,
                                        Func<string, Key> getShortKey,
                                        Func<Key, Key> convertToFullKey,
                                        Func<Key, string> getNVI,
                                        Func<Key, string> getRV)
        {
            List<Fragment> wordFragments = new List<Fragment>();
            int charIx = 0;
            int totalParagraphs = doc.Paragraphs.Count;
            int searchFails = 0;
            int unmanagedExceptions = 0;
            for (int paragraphIx = 1; paragraphIx <= totalParagraphs; paragraphIx++)
            {
                Logger.WriteLine("{0}/{1} ... {2}/{3}", paragraphIx, totalParagraphs, searchFails, unmanagedExceptions);
                var paragraph = doc.Paragraphs[paragraphIx];
                string text = paragraph.Range.Text;
                if (test(text))
                {
                    ExtractParsing(
                        text,
                        getVerseCharsCount,
                        getShortKey,
                        convertToFullKey,
                        ref charIx,
                        wordFragments,
                        ref searchFails,
                        ref unmanagedExceptions,
                        getNVI,
                        getRV);
                }
                charIx += paragraph.Range.Characters.Count;
            }
            return wordFragments;
        }

        public List<Fragment> ExtractWithRegex(Func<string, bool> test,
                                        Func<Key, int> getVerseCharsCount,
                                        Func<string, Key> getShortKey,
                                        Func<Key, Key> convertToFullKey,
                                        Func<Key, string> getNVI,
                                        Func<Key, string> getRV)
        {
            List<Fragment> wordFragments = new List<Fragment>();
            //int charIx = 0;
            int totalParagraphs = doc.Paragraphs.Count;
            //int searchFails = 0;
            //int unmanagedExceptions = 0;
            //(?<1>^\d?\s?\w{1,}\s+\d{1,3}:{1}\d{1,3})\s?(?<2>.{1,})\s?\(.*\d?\s+\w+\s+\d{1,3}:{1}\d{1,3}\)
            Regex rxcomp = new Regex(@"(?<1>^\*?\d?\s?\w{1,}\s+\d{1,3}:{1}\d{1,3})\s?(?<2>.{1,})\s*(?<3>\({1}.*\){1})", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            Regex rxsimp = new Regex(@"(?<1>^\*?\d?\s?\w{1,}\s+\d{1,3}:{1}\d{1,3})(?<2>.{1,})\r", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            for (int paragraphIx = 1; paragraphIx <= totalParagraphs; paragraphIx++)
            {
                //Logger.WriteLine("{0}/{1} ... {2}/{3}", paragraphIx, totalParagraphs, searchFails, unmanagedExceptions);
                var paragraph = doc.Paragraphs[paragraphIx];
                string text = paragraph.Range.Text;
                MatchCollection matches = rxcomp.Matches(text);
                if (matches.Count > 0)
                {
                    foreach (Match match in matches)
                    {
                        string referencia = match.Groups["1"].Value.Trim();
                        string texto = match.Groups["2"].Value.Trim();
                        string pie = match.Groups["3"].Value.Trim();

                        Logger.WriteLine("{0} ==> {1} ==> {2}", referencia, texto, pie);

                        paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdYellow;
                        string replacement = getRV(convertToFullKey(getShortKey(referencia)));
                        paragraph.Range.Text = paragraph.Range.Text.Replace(texto, replacement);

                        Console.WriteLine("   Original: [{0}]", texto);
                        Console.WriteLine("Replacement: [{0}]", replacement);
                    }
                }
                else
                {
                    matches = rxsimp.Matches(text);
                    if (matches.Count > 0)
                    {
                        foreach (Match match in matches)
                        {
                            string referencia = match.Groups["1"].Value.Trim();
                            string texto = match.Groups["2"].Value.Trim();

                            Logger.WriteLine("{0} --> {1}", referencia, texto);
                            string replacement = "FAIL!";
                            try
                            {
                                replacement = getRV(convertToFullKey(getShortKey(referencia)));
                                paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdYellow;
                                paragraph.Range.Text = paragraph.Range.Text.Replace(texto, replacement);
                            }
                            catch
                            {
                                paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdRed;
                            }

                            Console.WriteLine("   Original: [{0}]", texto);
                            Console.WriteLine("Replacement: [{0}]", replacement);
                        }
                    }
                }
            }
            return wordFragments;
        }

        public void ReplaceParagraphsText(Func<string, string> search, Func<string, string> replace)
        {
            sectionsParagraphs.ForEach(
                x =>
                {
                    var range = doc.Paragraphs[x.Item1].Range;
                    string toReplace = search(range.Text);
                    //try
                    {
                        string replacement = replace(toReplace);
                        if (replacement != null)
                        {
                            Logger.WriteLine("Replacing: =================================");
                            Logger.WriteLine("  Original: {0}", toReplace);
                            Logger.WriteLine("Replcement: {0}", replacement);
                            range.Text = range.Text.Replace(toReplace, replacement);
                        }
                    }
                    //catch
                    //{
                    //    Logger.WriteLine("Replacing: !! FAIL !!!!!!!!!!!!!!!!!!!!!!!!!");
                    //    Logger.WriteLine("  Original: {0}", toReplace);
                    //}
                });
        }

        public void ReplaceText2(Func<Key, string> replace, List<Fragment> fragments)
        {
            //fragments.Reverse();
            fragments.ForEach(
                x =>
                {
                    var range = doc.Range(x.Begin, x.End);
                    try
                    {
                        string replacement = replace(x.Key);
                        if (replacement != null)
                        {
                            Logger.WriteLine("Replacing: {0} =================================", x.Key);
                            Logger.WriteLine("  Original: {0}", x.Text);
                            Logger.WriteLine("Replcement: {0}", replacement);
                            //range.Text = range.Text.Replace(x.Item4/*to replace*/, replacement);
                            range.Select();
                            //range.Text = replacement;
                        }
                    }
                    catch
                    {
                        Logger.WriteLine("Replacing: !! FAIL !!!!!!!!!!!!!!!!!!!!!!!!!");
                        Logger.WriteLine("  Original: {0}", x.Text);
                    }
                });
        }

        public void HighlightText()
        {
            sectionsParagraphs.ForEach(
                x =>
                {
                    var range = doc.Paragraphs[x.Item1].Range;
                    range.HighlightColorIndex = Word.WdColorIndex.wdPink;
                });
        }

        public void LoadData(Action<string> action)
        {
            int totalParagraphs = doc.Paragraphs.Count;
            for (int paragraphIx = 1; paragraphIx <= totalParagraphs; paragraphIx++)
            {
                Logger.WriteLine("{0}/{1}", paragraphIx, totalParagraphs);
                var paragraph = doc.Paragraphs[paragraphIx];
                action(paragraph.Range.Text);
            }
        }

        public void ReplaceEntriesWithRegex(Func<string, Tuple<int, string, string>> searchEntryReplacement, Func<int, int> rvIdFromNVIId/*, Func<int, string> rvEntryFromNVIId*/)
        {
            int totalParagraphs = doc.Paragraphs.Count;
            Regex rx = new Regex(@"(?<1>\d+)–(?<2>.+)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            for (int paragraphIx = 1; paragraphIx <= totalParagraphs; paragraphIx++)
            {
                Paragraph paragraph = doc.Paragraphs[paragraphIx];
                //paragraph.Range.Find.ClearFormatting();
                //paragraph.Range.Find.Format = true;
                //paragraph.Range.Find.Font.Bold = 1;
                //paragraph.Range.Find.Execute(
                string fullText = paragraph.Range.Text;
                //string text = "";//paragraph.Range.Text;
                //for (int i = 1; i <= paragraph.Range.Words.Count; i++)
                //{
                //    int bold = paragraph.Range.Words[i].Bold;
                //    if (bold > 0)
                //        text += paragraph.Range.Words[i].Text;
                //}
                MatchCollection matches = rx.Matches(fullText);
                if (matches.Count > 0)
                {
                    foreach (Match match in matches)
                    {
                        try
                        {
                            int nviId = int.Parse(match.Groups["1"].Value.Trim());
                            int rvId = rvIdFromNVIId(nviId);
                            string nviText = match.Groups["2"].Value.Trim();

                            Logger.WriteLine("{0} ==> {1}", nviId, nviText);

                            Tuple<int, string, string> replacement = searchEntryReplacement(nviText);
                            if (replacement != null)
                            {
                                if (replacement.Item1 == rvId)
                                {
                                    paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdGreen;
                                    fullText = fullText.Replace(nviId.ToString(), rvId.ToString()).Replace(replacement.Item2, replacement.Item3);
                                    paragraph.Range.Text = fullText;
                                }
                                else
                                    paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdViolet;
                            }
                            else
                            {
                                Range numberRange = paragraph.Range.Words[1];
                                //paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdBrightGreen;
                                numberRange.HighlightColorIndex = Word.WdColorIndex.wdBrightGreen;
                                string numberText = numberRange.Text;
                                //fullText = fullText.Replace(nviId.ToString(), rvId.ToString());
                                numberText = numberText.Replace(nviId.ToString(), rvId.ToString());
                                //paragraph.Range.Text = fullText;
                                numberRange.Text = numberText;
                            }
                        }
                        catch { paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdRed; }
                    }
                }
            }
        }

        private string CleanNumber(string numberText)
        {
            return numberText.Trim().Replace(".", "");
        }

        public void ReplaceVease(Func<int, int> rvIdFromNVIId)
        {
            int totalParagraphs = doc.Paragraphs.Count;
            for (int paragraphIx = 1; paragraphIx <= totalParagraphs; paragraphIx++)
            {
                Paragraph paragraph = doc.Paragraphs[paragraphIx];
                string fullText = paragraph.Range.Text;
                if (fullText.Contains("Véase"))
                {
                    string originalText = fullText;
                    if (!fullText.StartsWith("Véase"))
                        originalText = fullText = fullText.Split(new string[] { "Véase" }, StringSplitOptions.RemoveEmptyEntries)[1];

                    paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdBlue;

                    string[] parts = fullText.Split(new char[] { ';', ' ', '.', '-' });
                    parts.ToList().ForEach(part =>
                        {
                            try
                            {
                                int nviId;
                                if (int.TryParse(part, out nviId))
                                {
                                    int rvId = rvIdFromNVIId(nviId);
                                    Logger.WriteLine("{0} ==> {1}", nviId, rvId);
                                    fullText = fullText.Replace(nviId.ToString(), rvId.ToString());
                                }
                            }
                            catch (Exception ex)
                            {
                                paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdRed;
                            }
                        });

                    paragraph.Range.Text = paragraph.Range.Text.Replace(originalText, fullText);
                }
            }
        }

        public void ReplaceVeaseWithRegex(Func<int, int> rvIdFromNVIId)
        {
            int totalParagraphs = doc.Paragraphs.Count;
            // complicada para nada
            //Véase(?<all>\s?(?<text>[a-zA-Z\sáéíóú]+)\s(?<parentesis>\(\d+\))*\s*(?<nums>(?<num>\d*)-?)*;*)+
            Regex rx = new Regex(@"([\s\.\;\-](?<num>\d{1,4})[\s\.\;\-]?)+", RegexOptions.Compiled | RegexOptions.IgnoreCase);
            for (int paragraphIx = 1; paragraphIx <= totalParagraphs; paragraphIx++)
            {
                Paragraph paragraph = doc.Paragraphs[paragraphIx];
                string fullText = paragraph.Range.Text;
                if (fullText.Contains("Véase"))
                {
                    if (!fullText.StartsWith("Véase"))
                    {
                        paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdRed;
                    }
                    else
                    {
                        paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdBlue;
                        string[] parts = fullText.Split(new char[] { ';' });
                        parts.ToList().ForEach(part =>
                        {
                            MatchCollection matches = rx.Matches(part);
                            if (matches.Count > 0)
                            {
                                foreach (Match match in matches)
                                {
                                    try
                                    {
                                        string idText = CleanNumber(match.Groups["1"].Value);
                                        int nviId = int.Parse(idText);
                                        int rvId = rvIdFromNVIId(nviId);
                                        Logger.WriteLine("{0} ==> {1}", nviId, rvId);
                                        fullText = fullText.Replace(nviId.ToString(), rvId.ToString());
                                    }
                                    catch (Exception ex)
                                    {
                                        paragraph.Range.HighlightColorIndex = Word.WdColorIndex.wdRed;
                                    }
                                }
                            }
                        });

                        paragraph.Range.Text = fullText;
                    }
                }
            }
        }

        public void ReplaceWords(Dictionary<string, string> words)
        {
            words.ToList().ForEach(x =>
                {
                    string strSearch = x.Key;
                    string strReplace = x.Value;
                    Logger.WriteLine("Replacing {0} by {1}", strSearch, strReplace);
                    Range rngStory = doc.Range(ref m, ref m);
                    rngStory.Find.ClearFormatting();
                    rngStory.Find.Replacement.ClearFormatting();
                    rngStory.Find.Text = strSearch;
                    rngStory.Find.Replacement.Text = strReplace;
                    rngStory.Find.MatchWholeWord = true;
                    rngStory.Find.Wrap = WdFindWrap.wdFindContinue;

                    object replace = WdReplace.wdReplaceAll;

                    rngStory.Find.Execute(ref m, ref m, ref m, ref m, ref m, ref m, ref m, ref m, ref m, ref m, ref replace, ref m, ref m, ref m, ref m);
                });
        }
    }
}
